home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Networking / OT PAPServerSample / PAPPostScriptStuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-28  |  16.7 KB  |  578 lines  |  [TEXT/CWIE]

  1. /*
  2.     File: PAPPostScriptStuff.c
  3.     
  4.     By Rich Kubota
  5.     
  6.     
  7.     The following are routines used to support the handling of Postscript query 
  8.     processing.  These are my own routines implemented to provide support for
  9.     the postscript queries from a laserwriter client.  All of the postscript
  10.     parsing related functions are included in this file.  I'd like to warn the
  11.     reader that this code is written to handle some basic postscript queries as
  12.     sent by Apple LaserWriter Client prior to v8.5.
  13.     
  14.     The design of the routine presented here are to
  15.     1. recognize postscript queries as opposed to postscript data
  16.     2. parse postscript commands and return the default response.
  17.     
  18.     This code does not support other Postscript clients or handling of other
  19.     than the default responses.  This may not be sufficient to support your
  20.     printer and client.
  21.     
  22.     For more information on processing Postscript queries, contact Adobe for their
  23.     technote on this subject *** Get web address ***
  24.     
  25.     A RIP or printer spooler would have to handle queries in a different manner.
  26.     In order to respond to the queris the spooler code might already have the
  27.     desired response, or may have to delay response until it can query the
  28.     printer to obtain the correct response.  
  29.     
  30.     My thanks to Mark Fleming for his help with debugging this code.
  31.     
  32.     Change History
  33.     
  34.     1.    Fixed the prevPtr bug where I return the current packetPtr instead of the previous
  35.         packetPtr in the ProcessPSQuery function
  36.     0.  Moved PostScript parsing code to this file.
  37.     
  38. */
  39.  
  40. #include <ctype.h>
  41. #include "StringUtils.h"
  42. #include "PAPServerSample.h"
  43. #include "PAPPostScriptStuff.h"
  44.  
  45. extern PacketPtr    gTempPackPtr;
  46. char                gEOFStr[8] = kEOFStr;
  47. char                gBeginPSStr[8] = "\045\041PS";        // = "%!PS"
  48. char                qBeginQueryStr[8] = kBeginQueryStr;
  49. char                gEndStr[8] = kEndStr;
  50. char                gQueryStr[8] = kQueryStr;
  51. extern OTLIFO*        gFreeQ;    
  52. extern Boolean        gDone;
  53.  
  54.  
  55. // prototypes
  56.  
  57. Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched,
  58.                         SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll);
  59.  
  60.  
  61. /*
  62.     The TestDataIsPSQuery is used to determine whether an incoming packet is the initial packet
  63.     for a postscript query, or is a continuing packet of a prelvious query started with some 
  64.     previous packet.
  65. */
  66. Boolean TestDataIsPSQuery(PacketPtr packetPtr)
  67. {
  68.     MyEndpointRef     *theEp;
  69.     Boolean            result;
  70.  
  71.     theEp = packetPtr->theEp;
  72.  
  73.     if (TstInPSQueryFlag(theEp->flags))    // check if the endpoint is already processing a postscript
  74.         result = true;                    // query for which we have not reached the EndQuery
  75.     else
  76.     {
  77.         result = IsPacketAPSQuery(packetPtr);
  78.         if (result == true)
  79.         {
  80. #if SHOW_DEBUG_FLOW
  81.         
  82.             DebugStr("\p processing a postscript query;g");
  83. #endif
  84.             theEp->prevPtr = nil;    // initialize the prevPtr field to nil
  85.             SetInPSQueryFlag(theEp->flags);
  86.             theEp->psState = kLookingForEndStr;    // indicate that we have identified query beginning
  87.                                         // and that the next step is to look for the EndQuery string
  88.  
  89.                 // set the time Data In timestamp field
  90.             BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataIn, sizeof(OTTimeStamp));
  91.                 // reset the numBytesIn field to zero
  92.             theEp->numBytesIn = 0;
  93.             
  94.         }
  95.     }
  96.     
  97.     return result;
  98. }
  99.  
  100. /*
  101.     ProcessPSQuery as used in this sample looks for the ps end...query string, then
  102.     gets the response and and sends it
  103.     The routine is designed to be called at deferred task time and uses OTAllocMem
  104.     instead of NewPtr or NewHandle
  105.     
  106.     Note that this routine is responsible for enqueueing the processed packet ptr
  107.     to the freeQ, unless it appears that there is a partial match to a string that
  108.     we are looking for at the end of the buffer.  If so, then the packetPtr is
  109.     save with the endpoint ref to be used when the next incoming packet is processed
  110.     
  111.     In this routine, the first while loop is where the contents of the ps query are
  112.     analyzed until the end of the packet is read.  As each default response is read,
  113.     an individual OTSnd call is made to send the default response.  Each response will 
  114.     be made with the T_MORE flag bit set to keep the EOF bit from being sent in the
  115.     PAP responses. This routine does not queue multiple responses to send in a single
  116.     response.
  117.     
  118.     When the end of the packet has been reached, the routine exits the while loop 
  119.     and checks for the EOF indicator signaling the end of the query.  If the 
  120.     EOF is read, then the call is made to SendEmptyPacket, which will result in
  121.     an empty packet with the EOF bit set.  The Apple LaserWriter client requires
  122.     receiving a last packet that has the eof bit set to know that the query has been
  123.     completely processed by the printer/server (even if the printer/server has responded
  124.     to all of the query items).
  125. */
  126. OSStatus ProcessPSQuery(PacketPtr packetPtr)
  127. {
  128.     OSStatus        err;
  129.     MyEndpointRef    *theEp;
  130.     PacketPtr        packPtr, tempPtr;
  131.     UInt16            offset;
  132.     UInt16            matchResult;
  133.     Boolean            done = false;
  134.     Boolean            testflag = false;
  135.  
  136.     theEp = packetPtr->theEp;
  137.         // add the number of bytes in the packet to the numBytesIn field
  138.     theEp->numBytesIn += packetPtr->numBytes;
  139.  
  140.         // set the time Data End timestamp field
  141.     BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataEnd, sizeof(OTTimeStamp));
  142.     
  143.     err = kOTNoError;
  144.         // check whether we need to concatenate the current and previous packet ptrs
  145.     if ((packetPtr == theEp->prevPtr) || (theEp->prevPtr == nil))
  146.     {
  147.         DebugStr("\p no packet to append");
  148.         testflag = false;
  149.         packPtr = packetPtr;
  150.     }
  151.     else
  152.     {
  153.         DebugStr("\p checking how we append packets");
  154.         packPtr = gTempPackPtr;
  155.  
  156.             // copy contents of the previous packet buffer to the new buffer
  157.         BlockMove((Ptr)theEp->prevPtr, (Ptr)packPtr, sizeof(PacketBuffer));
  158.         offset = sizeof(PacketBuffer) - kPAPDataSize + (theEp->prevPtr)->numBytes;
  159.         BlockMove((Ptr)&(packetPtr->data), (Ptr)&(packPtr->data[offset]), packetPtr->numBytes);
  160.             // adjust the numbytes field
  161.         packPtr->numBytes += packetPtr->numBytes;
  162.         
  163.             // enqueue the previous packet ptr back to the gFreeQ since I not expecting a 
  164.             // ps default response that overlaps 3 packets.
  165.             // fixed bug here where I release the current packetPtr instead of the previous
  166.             // packetPtr
  167.     
  168.         OTLIFOEnqueue(gFreeQ, &((theEp->prevPtr)->fLink));    // first field is fLink field
  169.     }
  170.     
  171.     while (done == false)
  172.     {
  173.         switch (theEp->psState)
  174.         {
  175.             case kLookingForEndStr:
  176.                 matchResult = FindQueryString(packPtr, kLookingForEndStr);
  177.                 break;
  178.  
  179.             case kLookingForQueryStr:
  180.                 matchResult = FindQueryString(packPtr, kLookingForQueryStr);
  181.                 break;
  182.  
  183.             case kLookingForDefaultResponse:
  184.                 if (testflag)
  185.                     DebugStr("\p about to call ProcessDefaultResponse");
  186.                 
  187.                 matchResult = ProcessDefaultResponse(packPtr);
  188.                 break;
  189.         }                
  190.                 
  191.         switch (matchResult)
  192.         {
  193.             case kMatch:
  194.                 if (theEp->psState == kLookingForDefaultResponse)
  195.                     theEp->psState = kLookingForEndStr;
  196.                 else
  197.                     theEp->psState += 1;    // increment the state
  198.                 break;
  199.             
  200.             case kPartialMatch:
  201.                 done = true;        // have to come around with the next packet
  202.                 break;
  203.  
  204.             case kNoMatch:
  205.                 DebugStr("\p no match");
  206.                                     // wasn't able to find a match, not even a 
  207.                                     // partial match, so set the pos to the end of the
  208.                                     // packet
  209.                 packPtr->lastPos = packPtr->numBytes;
  210.                 done = true;        // have to come around with the next packet
  211.                 break;
  212.         }
  213.  
  214.     }
  215.  
  216.         // check to see if we see the EOF flag
  217.         
  218.  
  219.         // this is the last packet of this query.
  220.         // check to see if the lastPos field is set to the end of the buffer
  221.         // this tels us the we are not awaiting a pending match for a partial
  222.         // string
  223.     if (packPtr->lastPos == packPtr->numBytes)
  224.     {
  225.             // reset the lastPos field so that we can search for the EOF string as the
  226.             // last few characters
  227.         packPtr->lastPos -= clen(gEOFStr);
  228.             // search for the EOF string at the end of the packet
  229.         matchResult = FindQueryString(packPtr, kLookingForEOFStr);
  230.             // since we reset lastPos, we need to restore it
  231.         packPtr->lastPos = packPtr->numBytes;
  232.             
  233.         if (matchResult == kMatch)
  234.         {
  235. #if SHOW_DEBUG_FLOW
  236.     
  237.             DebugStr("\p Sending a null packet;g");
  238. #endif
  239.                 // send an empty response with EOF flag set since this is what the Laserwriter 
  240.                 // client wants to see.
  241.             SendEmptyPacket(packPtr);
  242.                 // clear bit that indicates we are processing a postscript query
  243.             ClrInPSQueryFlag(theEp->flags);
  244. #if SHOW_DEBUG_FLOW
  245.     
  246.             DebugStr("\p Have finished ps query;g");
  247. #endif
  248.         }
  249.         
  250.     }
  251.         
  252.     
  253.         // check to see whether we processed all of the bytes in the packet.
  254.     if (packPtr->lastPos == packPtr->numBytes)
  255.     {
  256.         theEp->prevPtr = nil;    // if so, then don't save this packet in the prevPtr field.
  257.  
  258.                 // queue the buffer to the freeQ
  259.         OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink));    // first field is fLink field
  260.     }
  261.     else
  262.     {
  263.         theEp->prevPtr = packetPtr;    // we still have bytes to process.
  264.             //adjust the lastPos field
  265.         packetPtr->lastPos = packetPtr->numBytes - (packPtr->numBytes - packPtr->lastPos);
  266.         
  267.     }    
  268.         
  269.     return err;
  270.     
  271. }
  272.  
  273.  
  274. /*
  275.     DoProcessPSQuery is used as the entry point to processing a postscript query.
  276. */
  277. Boolean DoProcessPSQuery(PacketPtr    packetPtr)
  278. {
  279.     OSStatus    err = kOTNoError;
  280.     Boolean        result = false;
  281.         
  282.     if (TestDataIsPSQuery(packetPtr))  // is the packet a PS query
  283.     {
  284.         result = true;        // packet is a PostScript query and is being processed
  285.         err = ProcessPSQuery(packetPtr);
  286.             
  287.         if (err < kOTNoError)
  288.         {
  289.             DoValueBreak(err, "error occured calling ProcessPSQuery #");
  290.                 // show the error and quit the main event loop
  291.             gDone = true;
  292.         }
  293.         
  294.     }
  295.     else
  296.     {
  297.             // we aren't processing PS query packets, so exit this routine
  298.             // the IsPacketQuery routine potentially sets the lastPos field
  299.             // so reset it back to 0;
  300.         packetPtr->lastPos = 0;
  301.     }
  302.     
  303.     return result;
  304. }
  305.  
  306. /*
  307.     FindString is used to find the iterate through the characters passed in the buffer
  308.     to find the string passed in str.  The length of the search string is passed in 
  309.     lenStr.  pos is a pointer to the index where to start searching.  stopChar is the
  310.     last position of the buffer to search
  311.     input 
  312.         buffer - pointer to buffer to search
  313.         str - string to match
  314.         lenStr - length of the str to match in buffer
  315.         pos - index where to begin search
  316.         numCharsInBuffer - index of last valid char in buffer
  317.         matchAll - match both upper and lower cases of the input string.
  318.     
  319.     output -
  320.         lenMatched - number of character which were matched.  If no match, then
  321.                         returns 0.
  322.         pos - index of the next char to look at for the next search.
  323.         
  324.     If the str is not found in the buffer between the pos index and the stopChar,
  325.     *pos returns -1,  If a partial match is made at the end of the buffer, return the
  326.     negative offset
  327. */
  328.  
  329. Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched,
  330.                         SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll)
  331. {
  332.     SInt16        i;
  333.     SInt16        numToCompare;
  334.     Boolean        done = false;
  335.     Boolean        ok;
  336.     
  337.     numToCompare = lenStr;    // set the number of chars to compare as the string len.
  338.     while (done == false)    // search until the flag is set to true.
  339.     {
  340.             // check whether the first character matches the character at the
  341.             // current index position
  342.         if ((buffer[*pos] == str[0]) || ((matchAll == true) && (toupper(buffer[*pos]) == 
  343.                         toupper(str[0]))))
  344.         {
  345.             if (numCharsInBuffer < (*pos + lenStr))
  346.             {
  347.                     // the has too few remaining characters to compare all of the current
  348.                     // string.
  349.                 numToCompare = numCharsInBuffer - *pos;
  350.                 
  351.             }
  352.                 //init OK boolean
  353.             ok = true;
  354.             for (i = 1; (i < numToCompare) && ok; i++)
  355.             {
  356.                 if ((buffer[*pos+i] == str[i]) || ((matchAll == true) && 
  357.                         (toupper(buffer[*pos+1]) == toupper(str[i]))))
  358.                     continue; 
  359.                 else
  360.                     ok = false;
  361.                 
  362.             }
  363.                 // we've done our testing, now see if we found a match
  364.  
  365.             if (ok == true)        // was match found
  366.             {
  367.                 *lenMatched = numToCompare;        // return the number of chars matched
  368.                 *pos += numToCompare;            // return the next position in the buffer to start next search
  369.                 done = true;                    // search finished.
  370.             }
  371.         }
  372.         
  373.         if (done == false)
  374.         {
  375.             *pos += 1;        // no match found so increment the place to start the next search iteration
  376.             if ((*pos+1) == numCharsInBuffer)
  377.             {
  378.                     // no more characters in the buffer to search
  379.                 *lenMatched = 0;        // set len matched to nothing    
  380.                 done = true;            // search finished.
  381.             }
  382.         }
  383.         
  384.     }
  385.     
  386.     return (*lenMatched != 0);
  387. }
  388.  
  389. /*
  390.     IsPacketAPSQuery is used to check whether the contents of a packet are the beginning of
  391.     a PostScript query.  This can be found since there will be the signature stuff
  392.     at the beginning of the query.
  393. */
  394. Boolean IsPacketAPSQuery(PacketPtr packetPtr)
  395. {
  396.     SInt16        lenMatched, pos;
  397.     Boolean     result;
  398.     
  399.     pos = 0;        // start search from the beginning of the buffer
  400.     result = FindString((char*)&(packetPtr->data), gBeginPSStr, clen(gBeginPSStr),
  401.                 &lenMatched, &pos, 4, kCaseMatchAll);    // must find the "%!PS" string in position 3 & 4 of the buffer
  402.     if (result == true)
  403.     {
  404.             // the search found something in the first 4 characters of the buffer
  405.         if (lenMatched < clen(gBeginPSStr))
  406.         {
  407.             result = false;        // didn't match 2 characters
  408.         }
  409.         else
  410.         {
  411.             result = FindString((char*)&(packetPtr->data), qBeginQueryStr, 
  412.                             clen(qBeginQueryStr), &lenMatched, 
  413.                             &pos, 24, kCaseMatchAll);        // must find "Query\015" in pos 15 - 20 of the buffer
  414.             if (result == true)
  415.             {
  416.                 if (lenMatched < clen(qBeginQueryStr))
  417.                     result = false;
  418.                 else
  419.                 {
  420.                         // we're going to return a true result, to set the packetPtr.lastPos
  421.                         // field to the current pos field.  When we start our next search, we 
  422.                         // do so at this position.
  423.                     packetPtr->lastPos = pos;
  424.                 }
  425.             }
  426.         }
  427.     }
  428.     
  429.     return result;
  430. }
  431.  
  432. UInt16    FindQueryString(PacketPtr packetPtr, SInt16 whichStr)
  433. {
  434.     SInt16        lenMatched, pos;
  435.     UInt16        result1, len;
  436.     Boolean        result;
  437.     char        *str;
  438.     
  439.     result1 = kNoMatch;                // initialize to no Match
  440.     pos = packetPtr->lastPos;        // start search from where we last stopped looking in buffer
  441.                                     // first find the query prefix
  442.     
  443.     switch (whichStr)
  444.     {
  445.         case kLookingForEndStr:
  446.             str = gEndStr;
  447.             len = clen(gEndStr);
  448.             break;
  449.         
  450.         case kLookingForQueryStr:
  451.             str = gQueryStr;
  452.             len = clen(gQueryStr);
  453.             break;
  454.             
  455.         case kLookingForEOFStr:
  456.             str = gEOFStr;
  457.             len = clen(gEOFStr);
  458.             break;
  459.     }
  460.  
  461.     result = FindString((char*)&(packetPtr->data), str, len,
  462.                 &lenMatched, &pos, packetPtr->numBytes, kCaseMustMatch);    
  463.     
  464.     if (result == true)
  465.     {
  466.             // check that the proper string len was returned
  467.         if (lenMatched < len)
  468.         {
  469.                 // we have reached the end of the buffer and have found a partial match.
  470.             result1 = kPartialMatch;
  471.                 // set the lastPos field to where the partial match begins
  472.             packetPtr->lastPos = pos - lenMatched;
  473.         }
  474.         else
  475.         {
  476.                 // all parts found
  477.             result1 = kMatch;
  478.                 // complete match found, so advance the lastPos field
  479.             packetPtr->lastPos = pos;
  480.         }
  481.     }
  482.     
  483.     return result1;
  484.     
  485. }
  486.  
  487.  
  488. UInt16 ProcessDefaultResponse(PacketPtr packetPtr)
  489. {
  490.     OTResult    otErr;
  491.     UInt32        pos, begin;
  492.     UInt16        result;
  493.     char        response[255];
  494.  
  495.         // since we've found the end of the query chars, then there is at least a 
  496.         // partial match
  497.     result = kPartialMatch;
  498.         // set pos to the lastPos position since this is the beginning of 
  499.         // the query
  500.     begin = pos = packetPtr->lastPos;
  501.     
  502.         // remove the leading space character which may precede the default response
  503.     while (packetPtr->data[pos] == kSpaceChar)
  504.     {
  505.         pos++;
  506.         begin++;
  507.     }    
  508.     
  509.         // gather the default response characters    
  510.     while ((packetPtr->data[pos] != kReturnChar) && (pos < packetPtr->numBytes))
  511.     {
  512.             // gather default response characters
  513.         response[pos - begin] = packetPtr->data[pos];
  514.             // increment the counter
  515.         pos++;
  516.     }
  517.     
  518.         // go ahead and set the terminating null character for the "c" string
  519.     if ((pos - begin) < 255)
  520.         response[pos - begin] = 0;
  521.     else
  522.     {
  523.             // this should never happen, but if it does, I want to know about it.
  524. #if SHOW_DEBUG_FLOW
  525. //        DebugStr((const unsigned char *)"\p response greater than 255 chars");
  526. #endif
  527.         response[255] = 0;
  528.     }
  529.             
  530.     if (packetPtr->data[pos] == kReturnChar)
  531.     {
  532.             // we have found the end of the default response 
  533.             // catenate a line feed character to the  response 
  534.     
  535.         ccatchr(response, kLineFeedChar, 1);
  536.  
  537.         otErr = OTSnd((packetPtr->theEp)->ep, response, clen(response), T_MORE);
  538.         if (otErr < 0)
  539.         {
  540. #if SHOW_DEBUG_FLOW
  541.             DoValueBreak((long)otErr, "error occured sending PS response #");
  542. #endif
  543.         }
  544.         packetPtr->lastPos = pos;
  545.         result = kMatch;
  546.     }
  547.  
  548. #if SHOW_DEBUG_FLOW
  549.     ccatchr(response, ';', 1);
  550.     ccatchr(response, 'g', 1);
  551.     c2p(response);
  552. //    DebugStr((const unsigned char*)response);
  553. #endif
  554.  
  555.     
  556.     return result;
  557. }
  558.  
  559. /*
  560.     SendEmptyPacket is implemented to send an empty packet with the T_MORE flag not set.
  561.     The Apple LaserWriter client requires that the last response to a Postscript query
  562.     have the EOF flag set.  By having the EOM option enabled and sending a packet with 
  563.     the T_MORE flag not set, OT/PAP will send an empty packet that has the EOF bit set.
  564. */
  565. void SendEmptyPacket(PacketPtr packetPtr)
  566. {
  567.     OTResult    err;
  568.     char        response[16];
  569.  
  570.     err = OTSnd((packetPtr->theEp)->ep, &response, 0, 0);
  571.     if (err != kOTNoError)
  572.     {
  573. #if SHOW_DEBUG_FLOW
  574.         DoValueBreak((long)err, "error occured sending empty packet #");
  575. #endif
  576.     }
  577.     
  578. }